/* Copyright (C) 2007-2008  klevgert  http://code.google.com/p/db4o-netbeans/

This file is part of the db4o-netbeans module suite.

db4o-netbeans is free software; you can redistribute it and/or modify it under
the terms of version 2 of the GNU General Public License as published
by the Free Software Foundation and as clarified by db4objects' GPL 
interpretation policy, available at
http://www.db4o.com/about/company/legalpolicies/gplinterpretation/
Alternatively you can write to db4objects, Inc., 1900 S Norfolk Street,
Suite 350, San Mateo, CA 94403, USA.

db4o-netbeans is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. */

package com.db4o.nb.api.impl;

import com.db4o.ObjectContainer;
import com.db4o.nb.api.Db4oImporter;
import com.db4o.nb.util.ClassLoaderUtil;
import com.db4o.reflect.ReflectClass;
import com.db4o.reflect.ReflectField;
import java.io.File;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

/**
 *
 * @author klevgert
 */
public class XmlImporterImpl extends DefaultHandler implements Db4oImporter {
  
  protected ObjectContainer objectContainer; 
  
  // Arbitrary data
  protected PropertyData currentProp;
  protected InstanceData currentInstance;
  

  static private Writer  out;

  /** Creates a new instance of XmlImporterImpl */
  public XmlImporterImpl() {
  }

  public void importData(ObjectContainer target, String sourceFile) {
   
    this.objectContainer = target;
    
    // Use the default (non-validating) parser
    SAXParserFactory factory = SAXParserFactory.newInstance();
    try {

      this.out = new OutputStreamWriter(System.out, "UTF8");

      // Parse the input
      SAXParser saxParser = factory.newSAXParser();
      saxParser.parse( new File(sourceFile), this);

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

  public void startDocument() throws SAXException
  {
      emit("<?xml version='1.0' encoding='UTF-8'?>");
      nl();
  }

  public void endDocument() throws SAXException
  {
      try {
          nl();
          out.flush();
      } catch (IOException e) {
          throw new SAXException("I/O error", e);
      }
  }

  public void startElement(String namespaceURI,
                           String lName, // local name
                           String qName, // qualified name
                           Attributes attrs) throws SAXException
  {
    String eName = lName; // element name
    if (eName.equals("")) eName = qName; // namespaceAware = false
    emit("<"+eName);


    if( eName.equals("instance") && attrs != null) {

      this.currentInstance = new InstanceData();

      int idx = attrs.getIndex("class");
      if( idx >= 0 ) {
        this.currentInstance.className = attrs.getValue(idx);
      }
    }
    if( eName.equals("property") && this.currentInstance != null && attrs != null) {
      
      this.currentProp = new PropertyData();

      int idx = attrs.getIndex("class");
      if( idx >= 0 ) {
        this.currentProp.name = attrs.getValue(idx);
      }
      
    }

    if (attrs != null) {
      
      for (int i = 0; i < attrs.getLength(); i++) {
          String aName = attrs.getLocalName(i); // Attr name 
          if ("".equals(aName)) aName = attrs.getQName(i);
          emit(" ");
          emit(aName+"=\""+attrs.getValue(i)+"\"");
      }
    }
    emit(">");
  }

  public void endElement(String namespaceURI,
                         String sName, // simple name
                         String qName  // qualified name
                        ) throws SAXException
  {
      String eName = sName; // element name
      if ("".equals(eName)) eName = qName; // namespaceAware = false
      emit("</"+eName+">");
      
      if( eName.equals("instance") ) {

        // TODO take data and add to database
        storeInstance( this.currentInstance );
        
        this.currentInstance = null;
      }
      if( eName.equals("property") ) {
        
        this.currentInstance.properties.add( this.currentProp);
      }
   }

  public void characters(char buf[], int offset, int len) throws SAXException
  {
      String s = new String(buf, offset, len);
      
      if( this.currentProp != null ) {
        this.currentProp.value = s;
      }
      emit(s);
  }

  // Wrap I/O exceptions in SAX exceptions, to
  // suit handler signature requirements
  private void emit(String s) throws SAXException
  {
      try {
          out.write(s);
          out.flush();
      } catch (IOException e) {
          throw new SAXException("I/O error", e);
      }
  }

  // Start a new line
  private void nl() throws SAXException
  {
    String lineEnd =  System.getProperty("line.separator");
    try {
        out.write(lineEnd);
    } catch (IOException e) {
        throw new SAXException("I/O error", e);
    }
  }

  /**
   * Stores the gathered datato the database.
   */
  private void storeInstance(XmlImporterImpl.InstanceData currentInstance) {
    
    // Check pre condition
    if( this.objectContainer == null ) {
      return;
    }
    try {
      
      // Store instance data
        
      Class clazz = ClassLoaderUtil.getClassLoader().loadClass( this.currentInstance.className );
      Object inst = clazz.newInstance();
      this.objectContainer.set(inst);

      ReflectClass rc = this.objectContainer.ext().reflector().forClass(clazz);

      for (Iterator it = this.currentInstance.properties.iterator(); it.hasNext();) {
        PropertyData pd = (PropertyData) it.next();

        ReflectField rf = rc.getDeclaredField( pd.name );
        rf.setAccessible();
        rf.set( inst, pd.value );

      }
    } catch (InstantiationException ex) {
      ex.printStackTrace();
    } catch (IllegalAccessException ex) {
      ex.printStackTrace();
    } catch (ClassNotFoundException ex) {
      ex.printStackTrace();
    }

    
  }
  
  private class InstanceData {
    
    public String className = "";
    public List   properties = new ArrayList();
    
    public InstanceData() {
    }
  }
  
  private class PropertyData {
    
    public String name = "";
    public Object value;
    
    public PropertyData() {
    }
  }
}
